package com.ke.rule.base;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 决策表解析器
 * 
 * @author guyu
 *
 */
public class DecisionTableParser {
  
  /**
   * 解析决策表
   * 
   * @param file
   * @return
   */
  public static List<AssyFuncUnit> parseFile(File file) {
    Reader reader = null;
    try {
      reader = new FileReader(file);
      return parseFile(file.getPath(), reader).getRight();
    } catch (Exception e) {
      throw new IllegalArgumentException(String.format("parse file (%s) error: %s", file.getPath(),
          ExceptionUtils.getRootCauseMessage(e)));
    } finally {
      if (null != reader) {
        IOUtils.closeQuietly(reader);
      }
    }
  }
  
  /**
   * 解析决策表
   * @param reader
   * @param filePath
   * @return
   * @throws IOException 
   */
  private static Pair<Map<String, String>, List<AssyFuncUnit>> parseFile(String filePath, Reader reader) throws IOException {
    Iterable<CSVRecord> records = CSVFormat.EXCEL.parse(reader);
    return parseCsv(filePath, records);
  }
  
  /**
   * 解析决策表
   * 
   * @param file
   * @return
   */
  public static Pair<Map<String, String>, List<AssyFuncUnit>> parseFile(String file) {
    InputStream inputStream = DecisionTableParser.class.getResourceAsStream(file);
    
    Reader reader = null;
    try {
      reader = new InputStreamReader(inputStream);
      return parseFile(file, reader);
    } catch (Exception e) {
      throw new IllegalArgumentException(
          String.format("parse file (%s) error: %s", file, ExceptionUtils.getRootCauseMessage(e)));
    } finally {
      if (null != reader) {
        IOUtils.closeQuietly(reader);
      }
    }
  }
  
  /**
   * 解析决策表
   * @param filePath
   * @param records
   * @return
   */
  private static Pair<Map<String, String>, List<AssyFuncUnit>> parseCsv(String filePath, Iterable<CSVRecord> records) {
    Map<String, String> attrs = new HashMap<>();
    List<AssyFuncUnit> units = new ArrayList<>();
    
    int stepColIdx = -1;    // 步骤列的序号
    int specColIdx = -1;    // 规格列的序号
    
    boolean attrDefsEnded = false;
    boolean nextRowIsRule = false;
    for (CSVRecord record : records) {
      if (nextRowIsRule) {
        if (!record.isSet(stepColIdx)) {    // 规则行之间，或者文件末尾存在空行
          break;
        }
        String step = record.get(stepColIdx);
        if (StringUtils.isBlank(step)) {
          break;
        }
        String spec = record.isSet(specColIdx) ? record.get(specColIdx) : null;
        String excluded = record.isSet(specColIdx+1) ? record.get(specColIdx+1) : null;

        AssyFuncUnit bean = new AssyFuncUnit();
        bean.setStep(step);
        bean.setSpec(StringUtils.isBlank(spec) ? excluded : spec);
        bean.setExcluded(Boolean.valueOf(StringUtils.isNotBlank(excluded)));

        // 决策表的规则行中未找到功能部件的声明，打印警告信息
        if (StringUtils.isBlank(bean.getSpec())) {
          System.err.println(String.format("WARNING: FuncUnitSpec not found in decision table (file: %s, line: %d)", filePath, record.getRecordNumber()));
        }
        
        units.add(bean);
      } else {
        List<String> cells = new ArrayList<>();
        for (int colIdx = 0; colIdx < record.size(); colIdx++) {
          if (!record.isSet(colIdx)) {
            continue;
          }
          String value = record.get(colIdx);
          if (StringUtils.isBlank(value)) {
            continue;
          }
          cells.add(value);
          if (value.contains("步骤")) {
            stepColIdx = colIdx;
          } else if (value.contains("功能部件")) {
            specColIdx = colIdx;
            break;
          } else if (value.contains("RuleTable")) {
            attrDefsEnded = true;
          }
        }   // end for
        if (stepColIdx >= 0 && specColIdx >= 0) {
          nextRowIsRule = true;
        }
        if (!attrDefsEnded && (cells.size() == 2)) {
          attrs.put(cells.get(0).trim(), cells.get(1).trim());
        }
      } // end if
    }

    return Pair.of(attrs, units);
  }
}
